This notebook is Tom’s analysis from raw data

source("../CamProt_R/Utility.R")
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ tibble  2.0.0     ✔ purrr   0.2.5
✔ readr   1.3.1     ✔ stringr 1.3.1
✔ tibble  2.0.0     ✔ forcats 0.3.0
── Conflicts ───────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ MSnbase::combine()       masks Biobase::combine(), BiocGenerics::combine(), dplyr::combine()
✖ S4Vectors::expand()      masks tidyr::expand()
✖ dplyr::filter()          masks stats::filter()
✖ S4Vectors::first()       masks dplyr::first()
✖ dplyr::lag()             masks stats::lag()
✖ BiocGenerics::Position() masks ggplot2::Position(), base::Position()
✖ purrr::reduce()          masks MSnbase::reduce()
✖ S4Vectors::rename()      masks dplyr::rename()
infile <- "Input/OOPS_qLOPIT_LabelFree_PeptideGroups_parsed.txt"
samples_inf <- "Input/samples.tsv"
peptides_df <- parse_features(infile, silac=FALSE, TMT=FALSE, level="peptide", filter_crap=TRUE)
Tally of features at each stage:
29067   All features with a PSM
These features are associated with 2597 master proteins
27490   Excluding features without a master protein
These features are associated with 2596 master proteins
26424   Excluding features without a unique master protein
These features are associated with 2311 master proteins
26254   Excluding features matching a cRAP protein
These features are associated with 2301 master proteins
25987   Excluding features associated with a cRAP protein
These features are associated with 2283 master proteins
colnames(peptides_df)
 [1] "Checked"                           "Confidence"                        "Sequence"                         
 [4] "Modifications"                     "Qvality.PEP"                       "Qvality.q.value"                  
 [7] "Number.of.Protein.Groups"          "Number.of.Proteins"                "Number.of.PSMs"                   
[10] "Master.Protein.Accessions"         "Number.of.Missed.Cleavages"        "Theo.MHplus.in.Da"                
[13] "Found.in.Sample.in.S1.F1.Sample"   "Found.in.Sample.in.S2.F2.Sample"   "Found.in.Sample.in.S3.F3.Sample"  
[16] "Found.in.Sample.in.S4.F4.Sample"   "Found.in.Sample.in.S6.F6.Sample"   "Found.in.Sample.in.S5.F5.Sample"  
[19] "Found.in.Sample.in.S7.F7.Sample"   "Found.in.Sample.in.S8.F8.Sample"   "Found.in.Sample.in.S9.F9.Sample"  
[22] "Found.in.Sample.in.S10.F10.Sample" "Found.in.Sample.in.S11.F11.Sample" "Found.in.Sample.in.S12.F12.Sample"
[25] "Found.in.Sample.in.S13.F13.Sample" "Found.in.Sample.in.S14.F14.Sample" "Found.in.Sample.in.S15.F15.Sample"
[28] "Found.in.Sample.in.S16.F16.Sample" "Found.in.Sample.in.S17.F17.Sample" "Found.in.Sample.in.S18.F18.Sample"
[31] "Found.in.Sample.in.S19.F19.Sample" "Found.in.Sample.in.S20.F20.Sample" "Area.F1.Sample"                   
[34] "Area.F2.Sample"                    "Area.F3.Sample"                    "Area.F4.Sample"                   
[37] "Area.F6.Sample"                    "Area.F5.Sample"                    "Area.F7.Sample"                   
[40] "Area.F8.Sample"                    "Area.F9.Sample"                    "Area.F10.Sample"                  
[43] "Area.F11.Sample"                   "Area.F12.Sample"                   "Area.F13.Sample"                  
[46] "Area.F14.Sample"                   "Area.F15.Sample"                   "Area.F16.Sample"                  
[49] "Area.F17.Sample"                   "Area.F18.Sample"                   "Area.F19.Sample"                  
[52] "Area.F20.Sample"                   "XCorr.Sequest.HT"                  "Confidence.Sequest.HT"            
[55] "Percolator.q.Value.Sequest.HT"     "Percolator.PEP.Sequest.HT"         "master_protein"                   
[58] "protein_length"                    "protein_description"               "peptide_start"                    
[61] "peptide_end"                       "crap_protein"                      "associated_crap_protein"          
[64] "unique"                            "filename"                         
peptides_quant <- makeMSNSet(obj=peptides_df, samples_inf=samples_inf, ab_col_ix=2, level="peptide", quant_name="Area")

tallies for missing data (# samples with missing)
   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20 
  16   35   98   99  100  119  141  144  224  278  327  386  517  684  930 1238 1696 2517 3981 8158 4299 

4299 peptides do not have any quantification values

So lots and lots of missing values! most peptides are quantified in only a very minor subset of fractions (<5/20). This is no suprise since we’re dealing with LFQ and subcellular fractionation here.

plotMissing(peptides_quant)
Out of 21688 total features, 21672 (99.926%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
  35   98   99  100  119  141  144  224  278  327  386  517  684  930 1238 1696 2517 3981 8158 
And 21637 features have more than one missing value

What about if we subset to GO annotated RBPs?

human_go <- readRDS("./Input/h_sapiens_go_full.rds")
GO_RBPs <- human_go %>% filter(GO.ID=="GO:0003723") %>% pull(UNIPROTKB)
peptides_quant[fData(peptides_quant)$master_protein %in% GO_RBPs,] %>% plotMissing()
Out of 11023 total features, 11012 (99.9%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
  26   79   76   78   87  100  106  162  200  228  247  327  397  531  671  894 1253 1844 3706 
And 10986 features have more than one missing value

Ok, so this doesn’t make any difference, 99.9% have a missing value!

Let’s aggregate to the unique peptide sequence level (ignorning variable modifications) and then see whether that reduces our problem

peptides_no_mod_quant <- agg_to_peptides(peptides_quant)
Your data contains missing values. Please read the relevant section in the combineFeatures manual page for
details the effects of missing values on data aggregation.

Nope, not really! We still have 19304 features (previously 21688) and 99.9% have missing values!

plotMissing(peptides_no_mod_quant)
Out of 19304 total features, 19288 (99.917%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
  37  109  102   94  133  146  149  221  298  344  396  526  691  907 1204 1649 2389 3527 6366 
And 19251 features have more than one missing value

OK, so we’re going to have to impute. Note that the missing valuesa are particularly present in the first 2 fractions and across fractions 3-7. After that we see fewer missing values. Also, remember that we have yet to identify any definite set of RNAs from the initial fractions (1-7ish). For this reason, we’ll remove these first 5 fractions for now and leave fraction 6 & 7 as these are useful to separate light membranes

plot(colSums(is.na(exprs(peptides_no_mod_quant))))

#peptides_no_mod_quant_no_lm <- peptides_no_mod_quant[,8:20]
peptides_no_mod_quant_no_lm <- peptides_no_mod_quant[,6:20]
plotMissing(peptides_no_mod_quant_no_lm)
Out of 19304 total features, 19139 (99.145%) have missing values

   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15 
 148  196  227  277  304  338  485  603  808 1070 1564 2371 3506 6476  766 
And 18991 features have more than one missing value

Ok, so now we’re down to just 99.1% missing :p but at least we have some more peptides now with relatively few (<=4 missing values)

If we focus on those peptides with 4-9 missing values, we can see that many are missing in a block of sequential fractions. Arguably, these are true missing values, e.g not at random.

missing_n <- rowSums(is.na(exprs(peptides_no_mod_quant_no_lm)))
peptides_no_mod_quant_no_lm[(missing_n>=4 & missing_n<=9),] %>% plotMissing()
Out of 2815 total features, 2815 (100%) have missing values

  4   5   6   7   8   9 
277 304 338 485 603 808 
And 2815 features have more than one missing value

We can identify the missing values which are in a sequential block of >=5 fractions in a row and replace these with zero

First, let’s make a function to identify rows of values where the missing values are not random, e.g 4 or more in a row

test_values_1 <- c(1,1,NA,1,NA,NA,1,1,1,1)
test_values_2 <- c(1,1,NA,NA,NA,NA,NA,1,1,1)
test_values_3 <- c(NA,NA,NA,NA,1,1,1,1,1)
is_not_at_random <- function(x){
  with(rle(is.na(x)), sum(lengths[values] >= 4))
}
is_not_at_random(test_values_1)
[1] 0
is_not_at_random(test_values_2)
[1] 1
is_not_at_random(test_values_3)
[1] 1

Now, let’s check this identifies the peptides which are not missing at random. First, we’ll remove those without at least 4 quantification values.

peptides_no_mod_quant_4 <- peptides_no_mod_quant_no_lm[missing_n<=(ncol(peptides_no_mod_quant_no_lm)-4),]
missing_not_at_random <- apply(exprs(peptides_no_mod_quant_4), 1, is_not_at_random)
peptides_no_mod_quant_4[missing_not_at_random==1,] %>% plotMissing()
Out of 3846 total features, 3846 (100%) have missing values

   4    5    6    7    8    9   10   11 
  16   66  127  290  421  682  956 1288 
And 3846 features have more than one missing value

peptides_no_mod_quant_4[missing_not_at_random==2,] %>% plotMissing()
Out of 376 total features, 376 (100%) have missing values

  8   9  10  11 
  4  36  74 262 
And 376 features have more than one missing value

Now, let’s extend the function to replace the blocks of missing values with zero

test_values_1 <- c(1,1,NA,1,NA,NA,1,1,1,1)
test_values_2 <- c(1,1,NA,NA,NA,NA,NA,1,1,1)
test_values_3 <- c(NA,NA,NA,NA,1,1,1,1,1, NA)
rle(is.na(test_values_1))$values
[1] FALSE  TRUE FALSE  TRUE FALSE
rle(is.na(test_values_1))$lengths
[1] 2 1 1 2 4
replace_missing_not_at_random <- function(x){
  missing_rle <- rle(is.na(x))
  
  sequential_blocks <- missing_rle$values
  sequential_blocks[missing_rle$lengths<4] <- FALSE
  replace_with_zero <- rep(sequential_blocks, missing_rle$lengths)
  
  out <- x
  out[replace_with_zero]<-0
  
  return(out)
}
replace_missing_not_at_random(test_values_1)
 [1]  1  1 NA  1 NA NA  1  1  1  1
replace_missing_not_at_random(test_values_2)
 [1] 1 1 0 0 0 0 0 1 1 1
replace_missing_not_at_random(test_values_3)
 [1]  0  0  0  0  1  1  1  1  1 NA

Below we impute a maximum of 10 missing values in sequential blocks with zeros

missing_n2 <- rowSums(is.na(exprs(peptides_no_mod_quant_4)))
peptides_no_mod_quant_4_mnar_zero <- peptides_no_mod_quant_4[missing_n2<=10,]
exprs(peptides_no_mod_quant_4_mnar_zero) <- exprs(peptides_no_mod_quant_4_mnar_zero) %>%
  apply(1, replace_missing_not_at_random) %>% t()

Re-check the missing values

plotMissing(peptides_no_mod_quant_4)
Out of 6185 total features, 6020 (97.332%) have missing values

   1    2    3    4    5    6    7    8    9   10   11 
 148  196  227  277  304  338  485  603  808 1070 1564 
And 5872 features have more than one missing value

plotMissing(peptides_no_mod_quant_4_mnar_zero)
Out of 4621 total features, 4009 (86.756%) have missing values

  1   2   3   4   5   6   7   8   9  10 
668 789 734 593 429 293 195 178  90  40 
And 3341 features have more than one missing value

peptides_no_mod_quant_4_mnar_zero_mar_knn <- peptides_no_mod_quant_4_mnar_zero
imputed_zeros <- rowSums(exprs(peptides_no_mod_quant_4_mnar_zero_mar_knn)==0, na.rm=TRUE)
missing_n3 <- rowSums(is.na(exprs(peptides_no_mod_quant_4_mnar_zero_mar_knn)))
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation <- imputed_zeros>0 
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation_n <- imputed_zeros
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing <- missing_n3>0 
fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing_n <- missing_n3
peptides_no_mod_quant_4_mnar_zero_mar_knn <- peptides_no_mod_quant_4_mnar_zero_mar_knn[missing_n3<=3,]
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation))

FALSE  TRUE 
  736  2067 
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing))

FALSE  TRUE 
  612  2191 
print(table(fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,
            fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$zero_imputation))
       
        FALSE TRUE
  FALSE   165  447
  TRUE    571 1620
dim(peptides_no_mod_quant_4_mnar_zero)
[1] 4621   15
dim(peptides_no_mod_quant_4_mnar_zero_mar_knn)
[1] 2803   15
peptides_no_mod_quant_4_mnar_zero_mar_knn <- suppressMessages(impute(peptides_no_mod_quant_4_mnar_zero_mar_knn, "knn", k = 10))
Cluster size 2803 broken into 2633 170 
Cluster size 2633 broken into 2177 456 
Cluster size 2177 broken into 252 1925 
Done cluster 252 
Cluster size 1925 broken into 1390 535 
Done cluster 1390 
Done cluster 535 
Done cluster 1925 
Done cluster 2177 
Done cluster 456 
Done cluster 2633 
Done cluster 170 
p <- plotLabelQuant(peptides_no_mod_quant_no_lm, log=TRUE)

p <- plotLabelQuant(peptides_no_mod_quant_4_mnar_zero_mar_knn, log=TRUE)

p <- peptides_no_mod_quant_4_mnar_zero_mar_knn[
  fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,] %>% plotLabelQuant(log=TRUE)

p <- peptides_no_mod_quant_4_mnar_zero_mar_knn[
  !fData(peptides_no_mod_quant_4_mnar_zero_mar_knn)$missing,] %>% plotLabelQuant(log=TRUE)

protein_quant <- agg_to_protein(peptides_no_mod_quant_4_mnar_zero_mar_knn)
print(dim(protein_quant))
[1] 733  15
source("~/git_repos/CamProt_R/LOPIT.R")
markers_df <- read.delim("./Input//markers_9B_hyperLOPIT_vs_DC.csv", sep=",", header=FALSE, stringsAsFactors=FALSE)[,1:2]
markers_df$V2 <- recode(markers_df$V2, "RIBOSOME 40S"="RIBOSOME", "RIBOSOME 60S"="RIBOSOME")
markers_proteins <- markers_df$V2
names(markers_proteins) <- markers_df$V1
protein_quant_am <- addMarkers(normalise(protein_quant, "sum"), markers_proteins)
Markers in data: 123 out of 733
organelleMarkers
          CYTOSOL                ER             GOLGI          LYSOSOME      MITOCHONDRIA           NUCLEUS 
                8                21                 4                 2                 5                16 
NUCLEUS-CHROMATIN        PEROXISOME                PM        PROTEASOME          RIBOSOME           unknown 
                6                 1                18                 3                39               610 
p <- plotHexbin(protein_quant_am, "markers")
print(p)

marker_classes <- getMarkerClasses(protein_quant_am, "markers")
m_colours <- getColours(marker_classes)
p <- plotPCA(protein_quant_am, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2_inc_glycoproteins.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p

glycoproteins <- read.delim("./Input/glycoproteins.tsv")$protein

Copied from Manasa’s oopsFinal.Rmd notebook

# We will add a bit more information to the fData file 
# 1. List of known RBPs across cell lines in the XRNAX paper (Table S2)
xrnax = read.delim("./Input/xrnax-genelist.txt",sep="\t",header=T)
xrnax.rbps = xrnax %>% 
              dplyr::filter(!is.na(MCF7.RBP) | !is.na(HEK293.RBP) | !is.na(HeLa.RBP)) %>% 
              dplyr::select(Uniprot.ID:Protein.name)
rownames(xrnax.rbps) = xrnax.rbps$Uniprot.ID
print(length(rownames(xrnax.rbps)))
[1] 1753
# Check how many are common to the cell lines in the XRNAX paper
xrnax %>% 
  dplyr::select(MCF7.RBP:ihRBP) %>%
  apply(2, table,useNA="always")
            MCF7.RBP HEK293.RBP HeLa.RBP ihRBP
non-poly(A)      617        698      565   775
poly(A)          590        659      674   978
<NA>            1276       1126     1244   730
# 2. List of RBPs from SILAC experiments using OOPS after wash step2 (Table S1)
oops = read.delim("./Input/oops-genelist.txt",sep="\t",header=T)
oops.rbps = oops %>% 
              dplyr::filter(CL_NC_Ratio >= 1.0) %>% 
              dplyr::select(master_protein, RBP_glyco)
oops_rbps <- unique(oops.rbps$master_protein)
print(length(oops_rbps))
[1] 405
protein_quant_am_no_glyco <- protein_quant_am[!rownames(protein_quant_am) %in% glycoproteins,]
fData(protein_quant_am_no_glyco)$oops <- rownames(protein_quant_am_no_glyco) %in% oops_rbps
fData(protein_quant_am_no_glyco)$xrnax <- rownames(protein_quant_am_no_glyco) %in% rownames(xrnax.rbps)
fData(protein_quant_am_no_glyco)$go_rbp <- rownames(protein_quant_am_no_glyco) %in% GO_RBPs
print(table(fData(protein_quant_am_no_glyco)$oops, fData(protein_quant_am_no_glyco)$xrnax))
       
        FALSE TRUE
  FALSE   176  198
  TRUE     21  239
print(table(fData(protein_quant_am_no_glyco)$oops, fData(protein_quant_am_no_glyco)$xrnax,
            fData(protein_quant_am_no_glyco)$go_rbp))
, ,  = FALSE

       
        FALSE TRUE
  FALSE   149  102
  TRUE     16   31

, ,  = TRUE

       
        FALSE TRUE
  FALSE    27   96
  TRUE      5  208
print(dim(protein_quant_am))
[1] 733  15
print(dim(protein_quant_am_no_glyco))
[1] 634  15
marker_classes <- getMarkerClasses(protein_quant_am_no_glyco, "markers")
m_colours <- getColours(marker_classes)
p <- plotPCA(protein_quant_am_no_glyco, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am_no_glyco, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_3_4.png")
Saving 7.29 x 4.5 in image

protein_quant_am_no_glyco_yes_rbp <- protein_quant_am_no_glyco[
  (fData(protein_quant_am_no_glyco)$oops |
     fData(protein_quant_am_no_glyco)$xrnax |
    fData(protein_quant_am_no_glyco)$go_rbp),]
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order)
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps.png")
Saving 7.29 x 4.5 in image

p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, dims=c(3,4))
the condition has length > 1 and only the first element will be used
print(p)
$p
ggsave("./Output/plots/pca_3_4_no_glyco_only_rbps.png")
Saving 7.29 x 4.5 in image

translocon <- human_go %>% filter(GO.ID=='GO:0071256') %>% pull(UNIPROTKB)
para <- human_go %>% filter(GO.ID=='GO:0042382') %>% pull(UNIPROTKB)
 
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, foi=translocon)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps_translocon.png")
Saving 7.29 x 4.5 in image

print(dim(protein_quant_am_no_glyco_yes_rbp))
[1] 485  15
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "markers", m_colours=m_colours, re_order_markers=TRUE, marker_levels=organelle_order, foi=para)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

ggsave("./Output/plots/pca_1_2_no_glyco_only_rbps_paraspeckles.png")
Saving 7.29 x 4.5 in image

p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "markers", keep_markers=marker_classes, organelle_order=organelle_order) +
  facet_wrap(~markers) +
  scale_colour_manual(values=m_colours, guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

ggsave("./Output/plots/marker_profiles.png")
Saving 10 x 10 in image
p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "markers", keep_markers=marker_classes,
                        organelle_order=organelle_order, unknown=TRUE) +
  facet_grid(zero_imputation_n~missing_n) +
  scale_colour_manual(values=m_colours, guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

ggsave("./Output/plots/marker_profiles_imputation.png")
Saving 10 x 10 in image

Now we make a new set of markers designed to highlight functional groups of RBPs more usefully. First we need to define new sets of GO annotation proteins, where each marker belongs to only one group

translocon <- human_go %>% filter(GO.ID=='GO:0071256') %>% pull(UNIPROTKB)
para <- human_go %>% filter(GO.ID=='GO:0042382') %>% pull(UNIPROTKB)
mrna_splicing <- human_go %>% filter(GO.ID=='GO:0000398') %>% pull(UNIPROTKB)
translation_init <- human_go %>% filter(GO.ID=='GO:0006413') %>% pull(UNIPROTKB)
translation_init <- setdiff(translation_init, names(markers_proteins)[markers_proteins=="RIBOSOME"])
cell_cell_adhesion <- human_go %>% filter(GO.ID=='GO:0098609') %>% pull(UNIPROTKB)
cytoskeleton <- human_go %>% filter(GO.ID=='GO:0005856') %>% pull(UNIPROTKB)
motor_activity <- human_go %>% filter(GO.ID=='GO:0003774') %>% pull(UNIPROTKB)
er_stress_response <- human_go %>% filter(GO.ID=='GO:0030968') %>% pull(UNIPROTKB)
nuclear_pore_channel <- human_go %>% filter(GO.ID=='GO:0044613') %>% pull(UNIPROTKB)
nuclear_pore_basket <- human_go %>% filter(GO.ID=='GO:0044615') %>% pull(UNIPROTKB)
tRNA_AA <- human_go %>% filter(GO.ID=='GO:0004812') %>% pull(UNIPROTKB)
#mrna_splicing <- setdiff(mrna_splicing, c(para, translation_init, cell_cell_adhesion, cytoskeleton, motor_activity))
#translation_init <- setdiff(translation_init, c(para, cell_cell_adhesion, cytoskeleton, motor_activity))
#cytoskeleton <- setdiff(cytoskeleton, c(para, cell_cell_adhesion, motor_activity))
#cell_cell_adhesion <- setdiff(cell_cell_adhesion, c(para, motor_activity))
#all_markers <- c(mrna_splicing, para, translocon, translation_init, cell_cell_adhesion, cytoskeleton, motor_activity)
#print(length(all_markers)==length(unique(all_markers)))
mrna_splicing <- setdiff(mrna_splicing, tRNA_AA)
all_markers <- c(mrna_splicing, tRNA_AA)
print(length(all_markers)==length(unique(all_markers)))
[1] TRUE
rbps_markers <- markers_proteins
localisations_to_remove <- c("PEROXISOME", "PROTEASOME", "GOLGI", "LYSOSOME")
rbps_markers <- rbps_markers[!rbps_markers %in% localisations_to_remove]
rbps_markers[rbps_markers =="NUCLEUS-CHROMATIN"] <- "NUCLEUS"
print(table(rbps_markers))
rbps_markers
     CYTOSOL           ER MITOCHONDRIA      NUCLEUS           PM     RIBOSOME 
          96          118          197          171          295           72 
new_markers <- c(#rep("PARASPECKLES", length(para)),
                 rep("mRNA splicing", length(mrna_splicing)),
                 rep("Aminoacyl-tRNA ligase", length(tRNA_AA)),
                 "PARP1"#,
                 #rep("TRANSLATION INITITAION", length(translation_init)),
                 #rep("CELL-CELL ADHESION", length(cell_cell_adhesion)),
                 #rep("MOTOR", length(motor_activity))#,
                 #rep("CYTOSKELETON", length(cytoskeleton))
                 )
names(new_markers) <- c(#para,
                        mrna_splicing,
                        tRNA_AA,
                        "P09874"#,
                        #translation_init,
                        #cell_cell_adhesion,
                        #motor_activity#,
                        #cytoskeleton
                        )
print(table(names(new_markers))[table(names(new_markers))>1])
named integer(0)
rbps_markers <- rbps_markers[!names(rbps_markers) %in% names(new_markers)]
combined_markers <- c(rbps_markers, new_markers)
print(table(combined_markers))
combined_markers
Aminoacyl-tRNA ligase               CYTOSOL                    ER          MITOCHONDRIA         mRNA splicing 
                   42                    93                   118                   193                   321 
              NUCLEUS                 PARP1                    PM              RIBOSOME 
                  149                     1                   295                    72 
print(table(names(combined_markers))[table(names(combined_markers))>1])
named integer(0)
fData(protein_quant_am_no_glyco_yes_rbp)$new_markers <- NULL
protein_quant_am_no_glyco_yes_rbp <- addMarkers(protein_quant_am_no_glyco_yes_rbp, combined_markers, "new_markers")
Markers in data: 168 out of 485
organelleMarkers
Aminoacyl-tRNA ligase               CYTOSOL                    ER          MITOCHONDRIA         mRNA splicing 
                   13                     5                     7                     2                    86 
              NUCLEUS                 PARP1                    PM              RIBOSOME               unknown 
                   12                     1                     3                    39                   317 
fData(protein_quant_am_no_glyco_yes_rbp)$new_markers <- recode(
  fData(protein_quant_am_no_glyco_yes_rbp)$new_markers, "NUCLEUS"="Nucleus", "RIBOSOME"="Ribosome", "CYTOSOL"="Cytosol",
  "MITOCHONDRIA"="Mitochondria") 
print(table(fData(protein_quant_am_no_glyco_yes_rbp)$new_markers))

Aminoacyl-tRNA ligase               Cytosol                    ER          Mitochondria         mRNA splicing 
                   13                     5                     7                     2                    86 
              Nucleus                 PARP1                    PM              Ribosome               unknown 
                   12                     1                     3                    39                   317 

After adding these new RBP markers, we only have 1 PM and 2 Mt proteins remaining. Let’s remove the PM protein

fData(protein_quant_am_no_glyco_yes_rbp)$new_markers[fData(protein_quant_am_no_glyco_yes_rbp)$new_markers=="PM"] <- "unknown"
new_markers_levels <- getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers")
p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "new_markers", re_order_markers=TRUE, marker_levels=new_markers_levels,
        m_colours=getClassColours()[c(1:5, 7, 10:20)], foi=nuclear_pore_channel)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

p <- plotPCA(protein_quant_am_no_glyco_yes_rbp, "new_markers", re_order_markers=TRUE, marker_levels=new_markers_levels,
        m_colours=getClassColours()[c(1:5, 7, 10:20)], foi=nuclear_pore_basket)
the condition has length > 1 and only the first element will be used
print(p)
$p

$p_foi

getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers")
[1] "Aminoacyl-tRNA ligase" "Cytosol"               "ER"                    "Mitochondria"         
[5] "mRNA splicing"         "Nucleus"               "PARP1"                 "Ribosome"             
set.seed(1)
proj <- make_proj("t-SNE", protein_quant_am_no_glyco_yes_rbp, "new_markers")
library(Hmisc)
Loading required package: lattice
Loading required package: survival

Attaching package: ‘survival’

The following object is masked from ‘package:robustbase’:

    heart

Loading required package: Formula

Attaching package: ‘Hmisc’

The following object is masked from ‘package:AnnotationDbi’:

    contents

The following objects are masked from ‘package:MSnbase’:

    impute, naplot

The following object is masked from ‘package:Biobase’:

    contents

The following objects are masked from ‘package:dplyr’:

    src, summarize

The following objects are masked from ‘package:base’:

    format.pval, units
proj_df <- proj$PCA_df
marker_levels <- setdiff(new_markers_levels, "unknown")
marker_levels <- marker_levels[c(2,3,4,6,8,1,5,7)]
#m_colours <- getStockcol()[c(1,7,4,3,2,5)]
m_colours <- c(cbPalette[c(6,3,2,4,8,7,5)], "grey20")
proj_df$markers <- factor(proj_df$new_markers, levels=marker_levels)
print(table(is.na(proj_df$markers)))

FALSE  TRUE 
  165   320 
proj_df$unknown <- proj_df$new_markers=="unknown"
p <- ggplot(proj_df, aes(X, Y, colour=markers)) +
    geom_point(aes(X, Y, colour=markers, alpha=unknown), size=2) +
    scale_alpha_manual(values=c(1,0.2), guide=F) +
    my_theme +
    scale_colour_manual(values=m_colours, na.value="grey60", name="", breaks=c(marker_levels)) +
    guides(colour = guide_legend(override.aes = list(alpha = 1, size=2))) +
    xlab("Dimension 1") + ylab("Dimension 2")
print(p)
ggsave("./Output/tSNE_RBP_markers.png")
Saving 7.29 x 4.5 in image

write.table(proj_df, "./Output/tSNE_projections.tsv", sep="\t", quote=FALSE, row.name=FALSE)
p <- PlotMarkerProfiles(protein_quant_am_no_glyco_yes_rbp, "new_markers",
                        keep_markers=getMarkerClasses(protein_quant_am_no_glyco_yes_rbp, "new_markers"), organelle_order=new_markers_levels) +
  facet_wrap(~markers) +
  scale_colour_manual(values=getClassColours()[c(1:5, 7, 10:20)], guide=FALSE) +
  theme(legend.position="none")
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
print(p)

protein_info <- read.delim("Input/Aggregated-proteins-2187-with-uniprot.tab") %>%
  dplyr::select(Entry, gene_name=Gene.names...primary..)
total.prot = readRDS("./Input/prot_res_20_fractions_imputed_markers.rds")
colnames(total.prot)[12] <- "0.948"
colnames(total.prot) <- c(seq(1,20,2), seq(2,20,2))
total.prot <- total.prot[,order(as.numeric(as.character(colnames(total.prot))))]
total.prot <- total.prot[,5:19]
total.prot = normalise(total.prot,"sum")
rbp.prot <- protein_quant_am_no_glyco_yes_rbp
colnames(rbp.prot) <- 5:19
print(dim(total.prot))
[1] 5610   15
print(dim(rbp.prot))
[1] 485  15
print(colnames(total.prot))
 [1] "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
print(colnames(rbp.prot))
 [1] "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
plotCombinedProfiles <- function(foi){
  
  foi_in_total <- intersect(foi, rownames(total.prot))
  foi_in_rbp <- intersect(foi, rownames(rbp.prot))
  
  foi_in_both <- intersect(foi_in_rbp, foi_in_total)
  
  print(foi_in_both)
  if(length(foi_in_both)==0){
    return(NA)
  }
  
  total_exprs_df <- exprs(total.prot[foi_in_both,])
  total_exprs_df <- melt(total_exprs_df)
  total_exprs_df$type = "All protein"
  
  rbp_exprs_df <- exprs(rbp.prot[foi_in_both,])
  rbp_exprs_df <- melt(rbp_exprs_df)
  rbp_exprs_df$type = "RNA-bound"
  
  #print(head(total_exprs_df))
  #print(head(rbp_exprs_df))
  #print(head(rbind(rbp_exprs_df, total_exprs_df)))
  
  p <- rbind(rbp_exprs_df, total_exprs_df) %>%
    merge(protein_info, by.x="Var1", by.y="Entry") %>%
    ggplot(aes(Var2, value, colour=type, group=type)) +
    my_theme + geom_line() +
    theme(aspect.ratio=1, axis.text.x=element_text(angle=90, vjust=0.5, hjust=1))  +
    xlab("Fraction") + ylab("Sumn normalised\nabundance") + 
    scale_colour_discrete(name="", na.value="grey")
  
  if(length(foi_in_both)>1){
    p <- p + facet_wrap(~gene_name)
  }
  
  print(p)
  
  p2 <- p <- rbind(rbp_exprs_df, total_exprs_df) %>%
    merge(protein_info, by.x="Var1", by.y="Entry") %>%
    ggplot(aes(Var2, value, colour=type, group=interaction(type, gene_name))) +
    my_theme + geom_line() +
    theme(aspect.ratio=1, axis.text.x=element_text(angle=90, vjust=0.5, hjust=1))  +
    xlab("Fraction") + ylab("Sumn normalised\nabundance") + 
    scale_colour_discrete(name="", na.value="grey")
  
  print(p2)
  
  invisible(list("p"=p, "p2"=p2))
}
plotCombinedProfiles(tRNA_AA)
 [1] "Q9NSE4" "P41252" "P54577" "P49589" "P49591" "P14868" "O43776" "P07814" "P47897" "P26639" "P26640" "P49588"
[13] "P41250"

#plotCombinedProfiles(para[[2]])
plotCombinedProfiles(motor_activity)
[1] "Q14203" "P33176" "O00159" "Q14204" "P35579" "P52732" "Q13409" "P35580" "P60660"

fvarLabels(rbp.prot)
 [1] "Checked"                       "Confidence"                    "Sequence"                     
 [4] "Modifications"                 "Qvality.PEP"                   "Qvality.q.value"              
 [7] "Number.of.Protein.Groups"      "Number.of.Proteins"            "Number.of.PSMs"               
[10] "Master.Protein.Accessions"     "Number.of.Missed.Cleavages"    "Theo.MHplus.in.Da"            
[13] "XCorr.Sequest.HT"              "Confidence.Sequest.HT"         "Percolator.q.Value.Sequest.HT"
[16] "Percolator.PEP.Sequest.HT"     "master_protein"                "protein_length"               
[19] "protein_description"           "peptide_start"                 "peptide_end"                  
[22] "crap_protein"                  "associated_crap_protein"       "unique"                       
[25] "filename"                      "zero_imputation"               "zero_imputation_n"            
[28] "missing"                       "missing_n"                     "CV.F6"                        
[31] "CV.F7"                         "CV.F8"                         "CV.F9"                        
[34] "CV.F10"                        "CV.F11"                        "CV.F12"                       
[37] "CV.F13"                        "CV.F14"                        "CV.F15"                       
[40] "CV.F16"                        "CV.F17"                        "CV.F18"                       
[43] "CV.F19"                        "CV.F20"                        "markers"                      
[46] "oops"                          "xrnax"                         "go_rbp"                       
[49] "new_markers"                  
well_quantified_rbps <- fData(rbp.prot) %>%
  filter(zero_imputation_n<=4, missing_n<=2) %>%
  pull(master_protein)
plotCombinedProfiles(intersect(mrna_splicing, well_quantified_rbps))
[1] "Q01081" "Q00839" "P67809" "P11142" "O43290" "Q13247" "Q15366" "Q8IYB3"

ribosome_proteins <- names(markers_proteins)[markers_proteins=="RIBOSOME"]
plotCombinedProfiles(intersect(ribosome_proteins, well_quantified_rbps))
[1] "P15880" "P46781" "P35268" "P62750" "Q02543" "P36578" "P47914"

plotCombinedProfiles("Q15942")
[1] "Q15942"

p <- plotCombinedProfiles("P09874")
[1] "P09874"

ggsave("./Output/PARP1_profiles.png", p$p2)
Saving 7.29 x 4.5 in image

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBub3RlYm9vayBpcyBUb20ncyBhbmFseXNpcyBmcm9tIHJhdyBkYXRhCgpgYGB7cn0Kc291cmNlKCIuLi9DYW1Qcm90X1IvVXRpbGl0eS5SIikKbGlicmFyeSh0aWR5dmVyc2UpCmluZmlsZSA8LSAiSW5wdXQvT09QU19xTE9QSVRfTGFiZWxGcmVlX1BlcHRpZGVHcm91cHNfcGFyc2VkLnR4dCIKc2FtcGxlc19pbmYgPC0gIklucHV0L3NhbXBsZXMudHN2IgpgYGAKCmBgYHtyfQpwZXB0aWRlc19kZiA8LSBwYXJzZV9mZWF0dXJlcyhpbmZpbGUsIHNpbGFjPUZBTFNFLCBUTVQ9RkFMU0UsIGxldmVsPSJwZXB0aWRlIiwgZmlsdGVyX2NyYXA9VFJVRSkKYGBgCgpgYGB7cn0KY29sbmFtZXMocGVwdGlkZXNfZGYpCgpwZXB0aWRlc19xdWFudCA8LSBtYWtlTVNOU2V0KG9iaj1wZXB0aWRlc19kZiwgc2FtcGxlc19pbmY9c2FtcGxlc19pbmYsIGFiX2NvbF9peD0yLCBsZXZlbD0icGVwdGlkZSIsIHF1YW50X25hbWU9IkFyZWEiKQoKYGBgClNvIGxvdHMgYW5kIGxvdHMgb2YgbWlzc2luZyB2YWx1ZXMhIG1vc3QgcGVwdGlkZXMgYXJlIHF1YW50aWZpZWQgaW4gb25seSBhIHZlcnkgbWlub3Igc3Vic2V0IG9mIGZyYWN0aW9ucyAoPDUvMjApLiBUaGlzIGlzIG5vIHN1cHJpc2Ugc2luY2Ugd2UncmUgZGVhbGluZyB3aXRoIExGUSBhbmQgc3ViY2VsbHVsYXIgZnJhY3Rpb25hdGlvbiBoZXJlLgpgYGB7cn0KcGxvdE1pc3NpbmcocGVwdGlkZXNfcXVhbnQpCmBgYApXaGF0IGFib3V0IGlmIHdlIHN1YnNldCB0byBHTyBhbm5vdGF0ZWQgUkJQcz8KYGBge3J9Cmh1bWFuX2dvIDwtIHJlYWRSRFMoIi4vSW5wdXQvaF9zYXBpZW5zX2dvX2Z1bGwucmRzIikKR09fUkJQcyA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0iR086MDAwMzcyMyIpICU+JSBwdWxsKFVOSVBST1RLQikKYGBgCgpgYGB7cn0KcGVwdGlkZXNfcXVhbnRbZkRhdGEocGVwdGlkZXNfcXVhbnQpJG1hc3Rlcl9wcm90ZWluICVpbiUgR09fUkJQcyxdICU+JSBwbG90TWlzc2luZygpCmBgYApPaywgc28gdGhpcyBkb2Vzbid0IG1ha2UgYW55IGRpZmZlcmVuY2UsIDk5LjklIGhhdmUgYSBtaXNzaW5nIHZhbHVlIQoKTGV0J3MgYWdncmVnYXRlIHRvIHRoZSB1bmlxdWUgcGVwdGlkZSBzZXF1ZW5jZSBsZXZlbCAoaWdub3JuaW5nIHZhcmlhYmxlIG1vZGlmaWNhdGlvbnMpIGFuZCB0aGVuIHNlZSB3aGV0aGVyIHRoYXQgcmVkdWNlcyBvdXIgcHJvYmxlbQoKYGBge3J9CnBlcHRpZGVzX25vX21vZF9xdWFudCA8LSBhZ2dfdG9fcGVwdGlkZXMocGVwdGlkZXNfcXVhbnQpCmBgYAoKTm9wZSwgbm90IHJlYWxseSEgV2Ugc3RpbGwgaGF2ZSAxOTMwNCBmZWF0dXJlcyAocHJldmlvdXNseSAyMTY4OCkgYW5kIDk5LjklIGhhdmUgbWlzc2luZyB2YWx1ZXMhCmBgYHtyfQpwbG90TWlzc2luZyhwZXB0aWRlc19ub19tb2RfcXVhbnQpCmBgYAoKT0ssIHNvIHdlJ3JlIGdvaW5nIHRvIGhhdmUgdG8gaW1wdXRlLiBOb3RlIHRoYXQgdGhlIG1pc3NpbmcgdmFsdWVzYSBhcmUgcGFydGljdWxhcmx5IHByZXNlbnQgaW4gdGhlIGZpcnN0IDIgZnJhY3Rpb25zIGFuZCBhY3Jvc3MgZnJhY3Rpb25zIDMtNy4gQWZ0ZXIgdGhhdCB3ZSBzZWUgZmV3ZXIgbWlzc2luZyB2YWx1ZXMuIEFsc28sIHJlbWVtYmVyIHRoYXQgd2UgaGF2ZSB5ZXQgdG8gaWRlbnRpZnkgYW55IGRlZmluaXRlIHNldCBvZiBSTkFzIGZyb20gdGhlIGluaXRpYWwgZnJhY3Rpb25zICgxLTdpc2gpLiBGb3IgdGhpcyByZWFzb24sIHdlJ2xsIHJlbW92ZSB0aGVzZSBmaXJzdCA1IGZyYWN0aW9ucyBmb3Igbm93IGFuZCBsZWF2ZSBmcmFjdGlvbiA2ICYgNyBhcyB0aGVzZSBhcmUgdXNlZnVsIHRvIHNlcGFyYXRlIGxpZ2h0IG1lbWJyYW5lcwpgYGB7cn0KcGxvdChjb2xTdW1zKGlzLm5hKGV4cHJzKHBlcHRpZGVzX25vX21vZF9xdWFudCkpKSkKYGBgCgoKYGBge3J9CiNwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50Wyw4OjIwXQpwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50Wyw2OjIwXQpwbG90TWlzc2luZyhwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0pCmBgYApPaywgc28gbm93IHdlJ3JlIGRvd24gdG8gX2p1c3RfIDk5LjElIG1pc3NpbmcgOnAgYnV0IGF0IGxlYXN0IHdlIGhhdmUgc29tZSBtb3JlIHBlcHRpZGVzIG5vdyB3aXRoIHJlbGF0aXZlbHkgZmV3ICg8PTQgbWlzc2luZyB2YWx1ZXMpCgpJZiB3ZSBmb2N1cyBvbiB0aG9zZSBwZXB0aWRlcyB3aXRoIDQtOSBtaXNzaW5nIHZhbHVlcywgd2UgY2FuIHNlZSB0aGF0IG1hbnkgYXJlIG1pc3NpbmcgaW4gYSBibG9jayBvZiBzZXF1ZW50aWFsIGZyYWN0aW9ucy4gQXJndWFibHksIHRoZXNlIGFyZSB0cnVlIG1pc3NpbmcgdmFsdWVzLCBlLmcgbm90IGF0IHJhbmRvbS4gCmBgYHtyfQptaXNzaW5nX24gPC0gcm93U3Vtcyhpcy5uYShleHBycyhwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0pKSkKcGVwdGlkZXNfbm9fbW9kX3F1YW50X25vX2xtWyhtaXNzaW5nX24+PTQgJiBtaXNzaW5nX248PTkpLF0gJT4lIHBsb3RNaXNzaW5nKCkKYGBgCldlIGNhbiBpZGVudGlmeSB0aGUgbWlzc2luZyB2YWx1ZXMgd2hpY2ggYXJlIGluIGEgc2VxdWVudGlhbCBibG9jayBvZiA+PTUgZnJhY3Rpb25zIGluIGEgcm93IGFuZCByZXBsYWNlIHRoZXNlIHdpdGggemVybwoKRmlyc3QsIGxldCdzIG1ha2UgYSBmdW5jdGlvbiB0byBpZGVudGlmeSByb3dzIG9mIHZhbHVlcyB3aGVyZSB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIG5vdCByYW5kb20sIGUuZyA0IG9yIG1vcmUgaW4gYSByb3cKYGBge3J9CnRlc3RfdmFsdWVzXzEgPC0gYygxLDEsTkEsMSxOQSxOQSwxLDEsMSwxKQp0ZXN0X3ZhbHVlc18yIDwtIGMoMSwxLE5BLE5BLE5BLE5BLE5BLDEsMSwxKQp0ZXN0X3ZhbHVlc18zIDwtIGMoTkEsTkEsTkEsTkEsMSwxLDEsMSwxKQoKaXNfbm90X2F0X3JhbmRvbSA8LSBmdW5jdGlvbih4KXsKICB3aXRoKHJsZShpcy5uYSh4KSksIHN1bShsZW5ndGhzW3ZhbHVlc10gPj0gNCkpCn0KCmlzX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMSkKaXNfbm90X2F0X3JhbmRvbSh0ZXN0X3ZhbHVlc18yKQppc19ub3RfYXRfcmFuZG9tKHRlc3RfdmFsdWVzXzMpCmBgYAoKTm93LCBsZXQncyBjaGVjayB0aGlzIGlkZW50aWZpZXMgdGhlIHBlcHRpZGVzIHdoaWNoIGFyZSBub3QgbWlzc2luZyBhdCByYW5kb20uIEZpcnN0LCB3ZSdsbCByZW1vdmUgdGhvc2Ugd2l0aG91dCBhdCBsZWFzdCA0IHF1YW50aWZpY2F0aW9uIHZhbHVlcy4KYGBge3J9CnBlcHRpZGVzX25vX21vZF9xdWFudF80IDwtIHBlcHRpZGVzX25vX21vZF9xdWFudF9ub19sbVttaXNzaW5nX248PShuY29sKHBlcHRpZGVzX25vX21vZF9xdWFudF9ub19sbSktNCksXQptaXNzaW5nX25vdF9hdF9yYW5kb20gPC0gYXBwbHkoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpLCAxLCBpc19ub3RfYXRfcmFuZG9tKQpgYGAKCmBgYHtyfQpwZXB0aWRlc19ub19tb2RfcXVhbnRfNFttaXNzaW5nX25vdF9hdF9yYW5kb209PTEsXSAlPiUgcGxvdE1pc3NpbmcoKQpwZXB0aWRlc19ub19tb2RfcXVhbnRfNFttaXNzaW5nX25vdF9hdF9yYW5kb209PTIsXSAlPiUgcGxvdE1pc3NpbmcoKQpgYGAKTm93LCBsZXQncyBleHRlbmQgdGhlIGZ1bmN0aW9uIHRvIHJlcGxhY2UgdGhlIGJsb2NrcyBvZiBtaXNzaW5nIHZhbHVlcyB3aXRoIHplcm8KYGBge3J9CnRlc3RfdmFsdWVzXzEgPC0gYygxLDEsTkEsMSxOQSxOQSwxLDEsMSwxKQp0ZXN0X3ZhbHVlc18yIDwtIGMoMSwxLE5BLE5BLE5BLE5BLE5BLDEsMSwxKQp0ZXN0X3ZhbHVlc18zIDwtIGMoTkEsTkEsTkEsTkEsMSwxLDEsMSwxLCBOQSkKCgpybGUoaXMubmEodGVzdF92YWx1ZXNfMSkpJHZhbHVlcwpybGUoaXMubmEodGVzdF92YWx1ZXNfMSkpJGxlbmd0aHMKCnJlcGxhY2VfbWlzc2luZ19ub3RfYXRfcmFuZG9tIDwtIGZ1bmN0aW9uKHgpewogIG1pc3NpbmdfcmxlIDwtIHJsZShpcy5uYSh4KSkKICAKICBzZXF1ZW50aWFsX2Jsb2NrcyA8LSBtaXNzaW5nX3JsZSR2YWx1ZXMKICBzZXF1ZW50aWFsX2Jsb2Nrc1ttaXNzaW5nX3JsZSRsZW5ndGhzPDRdIDwtIEZBTFNFCgogIHJlcGxhY2Vfd2l0aF96ZXJvIDwtIHJlcChzZXF1ZW50aWFsX2Jsb2NrcywgbWlzc2luZ19ybGUkbGVuZ3RocykKICAKICBvdXQgPC0geAogIG91dFtyZXBsYWNlX3dpdGhfemVyb108LTAKICAKICByZXR1cm4ob3V0KQoKfQoKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMSkKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMikKcmVwbGFjZV9taXNzaW5nX25vdF9hdF9yYW5kb20odGVzdF92YWx1ZXNfMykKYGBgCgoKQmVsb3cgd2UgaW1wdXRlIGEgbWF4aW11bSBvZiAxMCBtaXNzaW5nIHZhbHVlcyBpbiBzZXF1ZW50aWFsIGJsb2NrcyB3aXRoIHplcm9zCmBgYHtyfQptaXNzaW5nX24yIDwtIHJvd1N1bXMoaXMubmEoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpKSkKcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvIDwtIHBlcHRpZGVzX25vX21vZF9xdWFudF80W21pc3NpbmdfbjI8PTEwLF0KCmV4cHJzKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVybykgPC0gZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvKSAlPiUKICBhcHBseSgxLCByZXBsYWNlX21pc3Npbmdfbm90X2F0X3JhbmRvbSkgJT4lIHQoKQpgYGAKClJlLWNoZWNrIHRoZSBtaXNzaW5nIHZhbHVlcwpgYGB7cn0KcGxvdE1pc3NpbmcocGVwdGlkZXNfbm9fbW9kX3F1YW50XzQpCnBsb3RNaXNzaW5nKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVybykKYGBgCgpgYGB7cn0KcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4gPC0gcGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvCgppbXB1dGVkX3plcm9zIDwtIHJvd1N1bXMoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pPT0wLCBuYS5ybT1UUlVFKQptaXNzaW5nX24zIDwtIHJvd1N1bXMoaXMubmEoZXhwcnMocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pKSkKCmZEYXRhKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uKSR6ZXJvX2ltcHV0YXRpb24gPC0gaW1wdXRlZF96ZXJvcz4wIApmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkemVyb19pbXB1dGF0aW9uX24gPC0gaW1wdXRlZF96ZXJvcwoKZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJG1pc3NpbmcgPC0gbWlzc2luZ19uMz4wIApmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZ19uIDwtIG1pc3NpbmdfbjMKCgpwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubiA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublttaXNzaW5nX24zPD0zLF0KCnByaW50KHRhYmxlKGZEYXRhKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uKSR6ZXJvX2ltcHV0YXRpb24pKQpwcmludCh0YWJsZShmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZykpCgpwcmludCh0YWJsZShmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZywKICAgICAgICAgICAgZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJHplcm9faW1wdXRhdGlvbikpCgpkaW0ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvKQpkaW0ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pCnBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uIDwtIHN1cHByZXNzTWVzc2FnZXMoaW1wdXRlKHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uLCAia25uIiwgayA9IDEwKSkKYGBgCgpgYGB7cn0KcCA8LSBwbG90TGFiZWxRdWFudChwZXB0aWRlc19ub19tb2RfcXVhbnRfbm9fbG0sIGxvZz1UUlVFKQpwIDwtIHBsb3RMYWJlbFF1YW50KHBlcHRpZGVzX25vX21vZF9xdWFudF80X21uYXJfemVyb19tYXJfa25uLCBsb2c9VFJVRSkKYGBgCgpgYGB7cn0KcCA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublsKICBmRGF0YShwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tubikkbWlzc2luZyxdICU+JSBwbG90TGFiZWxRdWFudChsb2c9VFJVRSkKcCA8LSBwZXB0aWRlc19ub19tb2RfcXVhbnRfNF9tbmFyX3plcm9fbWFyX2tublsKICAhZkRhdGEocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pJG1pc3NpbmcsXSAlPiUgcGxvdExhYmVsUXVhbnQobG9nPVRSVUUpCmBgYAoKYGBge3J9CnByb3RlaW5fcXVhbnQgPC0gYWdnX3RvX3Byb3RlaW4ocGVwdGlkZXNfbm9fbW9kX3F1YW50XzRfbW5hcl96ZXJvX21hcl9rbm4pCnByaW50KGRpbShwcm90ZWluX3F1YW50KSkKYGBgCgpgYGB7cn0Kc291cmNlKCJ+L2dpdF9yZXBvcy9DYW1Qcm90X1IvTE9QSVQuUiIpCmBgYAoKYGBge3J9Cm1hcmtlcnNfZGYgPC0gcmVhZC5kZWxpbSgiLi9JbnB1dC8vbWFya2Vyc185Ql9oeXBlckxPUElUX3ZzX0RDLmNzdiIsIHNlcD0iLCIsIGhlYWRlcj1GQUxTRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSlbLDE6Ml0KbWFya2Vyc19kZiRWMiA8LSByZWNvZGUobWFya2Vyc19kZiRWMiwgIlJJQk9TT01FIDQwUyI9IlJJQk9TT01FIiwgIlJJQk9TT01FIDYwUyI9IlJJQk9TT01FIikKbWFya2Vyc19wcm90ZWlucyA8LSBtYXJrZXJzX2RmJFYyCm5hbWVzKG1hcmtlcnNfcHJvdGVpbnMpIDwtIG1hcmtlcnNfZGYkVjEKCnByb3RlaW5fcXVhbnRfYW0gPC0gYWRkTWFya2Vycyhub3JtYWxpc2UocHJvdGVpbl9xdWFudCwgInN1bSIpLCBtYXJrZXJzX3Byb3RlaW5zKQoKcCA8LSBwbG90SGV4YmluKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIikKcHJpbnQocCkKYGBgCgoKYGBge3J9CgptYXJrZXJfY2xhc3NlcyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIikKbV9jb2xvdXJzIDwtIGdldENvbG91cnMobWFya2VyX2NsYXNzZXMpCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtLCAibWFya2VycyIsIG1fY29sb3Vycz1tX2NvbG91cnMsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1vcmdhbmVsbGVfb3JkZXIpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMl9pbmNfZ2x5Y29wcm90ZWlucy5wbmciKQoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW0sICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmBgYAoKYGBge3J9CmdseWNvcHJvdGVpbnMgPC0gcmVhZC5kZWxpbSgiLi9JbnB1dC9nbHljb3Byb3RlaW5zLnRzdiIpJHByb3RlaW4KYGBgCgpDb3BpZWQgZnJvbSBNYW5hc2EncyBvb3BzRmluYWwuUm1kIG5vdGVib29rCmBgYHtyfQojIFdlIHdpbGwgYWRkIGEgYml0IG1vcmUgaW5mb3JtYXRpb24gdG8gdGhlIGZEYXRhIGZpbGUgCiMgMS4gTGlzdCBvZiBrbm93biBSQlBzIGFjcm9zcyBjZWxsIGxpbmVzIGluIHRoZSBYUk5BWCBwYXBlciAoVGFibGUgUzIpCnhybmF4ID0gcmVhZC5kZWxpbSgiLi9JbnB1dC94cm5heC1nZW5lbGlzdC50eHQiLHNlcD0iXHQiLGhlYWRlcj1UKQp4cm5heC5yYnBzID0geHJuYXggJT4lIAogICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKE1DRjcuUkJQKSB8ICFpcy5uYShIRUsyOTMuUkJQKSB8ICFpcy5uYShIZUxhLlJCUCkpICU+JSAKICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KFVuaXByb3QuSUQ6UHJvdGVpbi5uYW1lKQpyb3duYW1lcyh4cm5heC5yYnBzKSA9IHhybmF4LnJicHMkVW5pcHJvdC5JRApwcmludChsZW5ndGgocm93bmFtZXMoeHJuYXgucmJwcykpKQoKIyBDaGVjayBob3cgbWFueSBhcmUgY29tbW9uIHRvIHRoZSBjZWxsIGxpbmVzIGluIHRoZSBYUk5BWCBwYXBlcgp4cm5heCAlPiUgCiAgZHBseXI6OnNlbGVjdChNQ0Y3LlJCUDppaFJCUCkgJT4lCiAgYXBwbHkoMiwgdGFibGUsdXNlTkE9ImFsd2F5cyIpCgojIDIuIExpc3Qgb2YgUkJQcyBmcm9tIFNJTEFDIGV4cGVyaW1lbnRzIHVzaW5nIE9PUFMgYWZ0ZXIgd2FzaCBzdGVwMiAoVGFibGUgUzEpCm9vcHMgPSByZWFkLmRlbGltKCIuL0lucHV0L29vcHMtZ2VuZWxpc3QudHh0IixzZXA9Ilx0IixoZWFkZXI9VCkKb29wcy5yYnBzID0gb29wcyAlPiUgCiAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihDTF9OQ19SYXRpbyA+PSAxLjApICU+JSAKICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KG1hc3Rlcl9wcm90ZWluLCBSQlBfZ2x5Y28pCgpvb3BzX3JicHMgPC0gdW5pcXVlKG9vcHMucmJwcyRtYXN0ZXJfcHJvdGVpbikKcHJpbnQobGVuZ3RoKG9vcHNfcmJwcykpCmBgYAoKYGBge3J9CnByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28gPC0gcHJvdGVpbl9xdWFudF9hbVshcm93bmFtZXMocHJvdGVpbl9xdWFudF9hbSkgJWluJSBnbHljb3Byb3RlaW5zLF0KZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkb29wcyA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIG9vcHNfcmJwcwpmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSR4cm5heCA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIHJvd25hbWVzKHhybmF4LnJicHMpCmZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJGdvX3JicCA8LSByb3duYW1lcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSAlaW4lIEdPX1JCUHMKCnByaW50KHRhYmxlKGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJG9vcHMsIGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJHhybmF4KSkKcHJpbnQodGFibGUoZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkb29wcywgZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljbykkeHJuYXgsCiAgICAgICAgICAgIGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28pJGdvX3JicCkpCgpwcmludChkaW0ocHJvdGVpbl9xdWFudF9hbSkpCnByaW50KGRpbShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSkKYGBgCmBgYHtyfQptYXJrZXJfY2xhc3NlcyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28sICJtYXJrZXJzIikKbV9jb2xvdXJzIDwtIGdldENvbG91cnMobWFya2VyX2NsYXNzZXMpCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvLCAibWFya2VycyIsIG1fY29sb3Vycz1tX2NvbG91cnMsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1vcmdhbmVsbGVfb3JkZXIpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMi5wbmciKQoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y28sICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzNfNC5wbmciKQpgYGAKYGBge3J9CnByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCA8LSBwcm90ZWluX3F1YW50X2FtX25vX2dseWNvWwogIChmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSRvb3BzIHwKICAgICBmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSR4cm5heCB8CiAgICBmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvKSRnb19yYnApLF0KCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlcikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9wY2FfMV8yX25vX2dseWNvX29ubHlfcmJwcy5wbmciKQoKCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZGltcz1jKDMsNCkpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzNfNF9ub19nbHljb19vbmx5X3JicHMucG5nIikKYGBgCgpgYGB7cn0KdHJhbnNsb2NvbiA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA3MTI1NicpICU+JSBwdWxsKFVOSVBST1RLQikKcGFyYSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0MjM4MicpICU+JSBwdWxsKFVOSVBST1RLQikKCiAKYGBgCgpgYGB7cn0KcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm1hcmtlcnMiLCBtX2NvbG91cnM9bV9jb2xvdXJzLCByZV9vcmRlcl9tYXJrZXJzPVRSVUUsIG1hcmtlcl9sZXZlbHM9b3JnYW5lbGxlX29yZGVyLCBmb2k9dHJhbnNsb2NvbikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9wY2FfMV8yX25vX2dseWNvX29ubHlfcmJwc190cmFuc2xvY29uLnBuZyIpCmBgYApgYGB7cn0KcHJpbnQoZGltKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkpCmBgYAoKYGBge3J9CnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwgbV9jb2xvdXJzPW1fY29sb3VycywgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW9yZ2FuZWxsZV9vcmRlciwgZm9pPXBhcmEpCnByaW50KHApCmdnc2F2ZSgiLi9PdXRwdXQvcGxvdHMvcGNhXzFfMl9ub19nbHljb19vbmx5X3JicHNfcGFyYXNwZWNrbGVzLnBuZyIpCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwIDwtIFBsb3RNYXJrZXJQcm9maWxlcyhwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJtYXJrZXJzIiwga2VlcF9tYXJrZXJzPW1hcmtlcl9jbGFzc2VzLCBvcmdhbmVsbGVfb3JkZXI9b3JnYW5lbGxlX29yZGVyKSArCiAgZmFjZXRfd3JhcCh+bWFya2VycykgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPW1fY29sb3VycywgZ3VpZGU9RkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwcmludChwKQpnZ3NhdmUoIi4vT3V0cHV0L3Bsb3RzL21hcmtlcl9wcm9maWxlcy5wbmciKQoKcCA8LSBQbG90TWFya2VyUHJvZmlsZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibWFya2VycyIsIGtlZXBfbWFya2Vycz1tYXJrZXJfY2xhc3NlcywKICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5lbGxlX29yZGVyPW9yZ2FuZWxsZV9vcmRlciwgdW5rbm93bj1UUlVFKSArCiAgZmFjZXRfZ3JpZCh6ZXJvX2ltcHV0YXRpb25fbn5taXNzaW5nX24pICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1tX2NvbG91cnMsIGd1aWRlPUZBTFNFKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcHJpbnQocCkKZ2dzYXZlKCIuL091dHB1dC9wbG90cy9tYXJrZXJfcHJvZmlsZXNfaW1wdXRhdGlvbi5wbmciKQpgYGAKCk5vdyB3ZSBtYWtlIGEgbmV3IHNldCBvZiBtYXJrZXJzIGRlc2lnbmVkIHRvIGhpZ2hsaWdodCBmdW5jdGlvbmFsIGdyb3VwcyBvZiBSQlBzIG1vcmUgdXNlZnVsbHkuIEZpcnN0IHdlIG5lZWQgdG8gZGVmaW5lIG5ldyBzZXRzIG9mIEdPIGFubm90YXRpb24gcHJvdGVpbnMsIHdoZXJlIGVhY2ggbWFya2VyIGJlbG9uZ3MgdG8gb25seSBvbmUgZ3JvdXAKYGBge3J9CnRyYW5zbG9jb24gPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwNzEyNTYnKSAlPiUgcHVsbChVTklQUk9US0IpCnBhcmEgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwNDIzODInKSAlPiUgcHVsbChVTklQUk9US0IpCm1ybmFfc3BsaWNpbmcgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwMDAzOTgnKSAlPiUgcHVsbChVTklQUk9US0IpCnRyYW5zbGF0aW9uX2luaXQgPC0gaHVtYW5fZ28gJT4lIGZpbHRlcihHTy5JRD09J0dPOjAwMDY0MTMnKSAlPiUgcHVsbChVTklQUk9US0IpCnRyYW5zbGF0aW9uX2luaXQgPC0gc2V0ZGlmZih0cmFuc2xhdGlvbl9pbml0LCBuYW1lcyhtYXJrZXJzX3Byb3RlaW5zKVttYXJrZXJzX3Byb3RlaW5zPT0iUklCT1NPTUUiXSkKCmNlbGxfY2VsbF9hZGhlc2lvbiA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA5ODYwOScpICU+JSBwdWxsKFVOSVBST1RLQikKY3l0b3NrZWxldG9uIDwtIGh1bWFuX2dvICU+JSBmaWx0ZXIoR08uSUQ9PSdHTzowMDA1ODU2JykgJT4lIHB1bGwoVU5JUFJPVEtCKQptb3Rvcl9hY3Rpdml0eSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDAwMzc3NCcpICU+JSBwdWxsKFVOSVBST1RLQikKZXJfc3RyZXNzX3Jlc3BvbnNlIDwtIGh1bWFuX2dvICU+JSBmaWx0ZXIoR08uSUQ9PSdHTzowMDMwOTY4JykgJT4lIHB1bGwoVU5JUFJPVEtCKQpudWNsZWFyX3BvcmVfY2hhbm5lbCA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0NDYxMycpICU+JSBwdWxsKFVOSVBST1RLQikKbnVjbGVhcl9wb3JlX2Jhc2tldCA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDA0NDYxNScpICU+JSBwdWxsKFVOSVBST1RLQikKdFJOQV9BQSA8LSBodW1hbl9nbyAlPiUgZmlsdGVyKEdPLklEPT0nR086MDAwNDgxMicpICU+JSBwdWxsKFVOSVBST1RLQikKCiNtcm5hX3NwbGljaW5nIDwtIHNldGRpZmYobXJuYV9zcGxpY2luZywgYyhwYXJhLCB0cmFuc2xhdGlvbl9pbml0LCBjZWxsX2NlbGxfYWRoZXNpb24sIGN5dG9za2VsZXRvbiwgbW90b3JfYWN0aXZpdHkpKQojdHJhbnNsYXRpb25faW5pdCA8LSBzZXRkaWZmKHRyYW5zbGF0aW9uX2luaXQsIGMocGFyYSwgY2VsbF9jZWxsX2FkaGVzaW9uLCBjeXRvc2tlbGV0b24sIG1vdG9yX2FjdGl2aXR5KSkKI2N5dG9za2VsZXRvbiA8LSBzZXRkaWZmKGN5dG9za2VsZXRvbiwgYyhwYXJhLCBjZWxsX2NlbGxfYWRoZXNpb24sIG1vdG9yX2FjdGl2aXR5KSkKI2NlbGxfY2VsbF9hZGhlc2lvbiA8LSBzZXRkaWZmKGNlbGxfY2VsbF9hZGhlc2lvbiwgYyhwYXJhLCBtb3Rvcl9hY3Rpdml0eSkpCgojYWxsX21hcmtlcnMgPC0gYyhtcm5hX3NwbGljaW5nLCBwYXJhLCB0cmFuc2xvY29uLCB0cmFuc2xhdGlvbl9pbml0LCBjZWxsX2NlbGxfYWRoZXNpb24sIGN5dG9za2VsZXRvbiwgbW90b3JfYWN0aXZpdHkpCiNwcmludChsZW5ndGgoYWxsX21hcmtlcnMpPT1sZW5ndGgodW5pcXVlKGFsbF9tYXJrZXJzKSkpCgoKbXJuYV9zcGxpY2luZyA8LSBzZXRkaWZmKG1ybmFfc3BsaWNpbmcsIHRSTkFfQUEpCgphbGxfbWFya2VycyA8LSBjKG1ybmFfc3BsaWNpbmcsIHRSTkFfQUEpCnByaW50KGxlbmd0aChhbGxfbWFya2Vycyk9PWxlbmd0aCh1bmlxdWUoYWxsX21hcmtlcnMpKSkKCmBgYAoKYGBge3J9CgpyYnBzX21hcmtlcnMgPC0gbWFya2Vyc19wcm90ZWlucwpsb2NhbGlzYXRpb25zX3RvX3JlbW92ZSA8LSBjKCJQRVJPWElTT01FIiwgIlBST1RFQVNPTUUiLCAiR09MR0kiLCAiTFlTT1NPTUUiKQoKcmJwc19tYXJrZXJzIDwtIHJicHNfbWFya2Vyc1shcmJwc19tYXJrZXJzICVpbiUgbG9jYWxpc2F0aW9uc190b19yZW1vdmVdCnJicHNfbWFya2Vyc1tyYnBzX21hcmtlcnMgPT0iTlVDTEVVUy1DSFJPTUFUSU4iXSA8LSAiTlVDTEVVUyIKCnByaW50KHRhYmxlKHJicHNfbWFya2VycykpCgoKbmV3X21hcmtlcnMgPC0gYygjcmVwKCJQQVJBU1BFQ0tMRVMiLCBsZW5ndGgocGFyYSkpLAogICAgICAgICAgICAgICAgIHJlcCgibVJOQSBzcGxpY2luZyIsIGxlbmd0aChtcm5hX3NwbGljaW5nKSksCiAgICAgICAgICAgICAgICAgcmVwKCJBbWlub2FjeWwtdFJOQSBsaWdhc2UiLCBsZW5ndGgodFJOQV9BQSkpLAogICAgICAgICAgICAgICAgICJQQVJQMSIjLAogICAgICAgICAgICAgICAgICNyZXAoIlRSQU5TTEFUSU9OIElOSVRJVEFJT04iLCBsZW5ndGgodHJhbnNsYXRpb25faW5pdCkpLAogICAgICAgICAgICAgICAgICNyZXAoIkNFTEwtQ0VMTCBBREhFU0lPTiIsIGxlbmd0aChjZWxsX2NlbGxfYWRoZXNpb24pKSwKICAgICAgICAgICAgICAgICAjcmVwKCJNT1RPUiIsIGxlbmd0aChtb3Rvcl9hY3Rpdml0eSkpIywKICAgICAgICAgICAgICAgICAjcmVwKCJDWVRPU0tFTEVUT04iLCBsZW5ndGgoY3l0b3NrZWxldG9uKSkKICAgICAgICAgICAgICAgICApCgpuYW1lcyhuZXdfbWFya2VycykgPC0gYygjcGFyYSwKICAgICAgICAgICAgICAgICAgICAgICAgbXJuYV9zcGxpY2luZywKICAgICAgICAgICAgICAgICAgICAgICAgdFJOQV9BQSwKICAgICAgICAgICAgICAgICAgICAgICAgIlAwOTg3NCIjLAogICAgICAgICAgICAgICAgICAgICAgICAjdHJhbnNsYXRpb25faW5pdCwKICAgICAgICAgICAgICAgICAgICAgICAgI2NlbGxfY2VsbF9hZGhlc2lvbiwKICAgICAgICAgICAgICAgICAgICAgICAgI21vdG9yX2FjdGl2aXR5IywKICAgICAgICAgICAgICAgICAgICAgICAgI2N5dG9za2VsZXRvbgogICAgICAgICAgICAgICAgICAgICAgICApCnByaW50KHRhYmxlKG5hbWVzKG5ld19tYXJrZXJzKSlbdGFibGUobmFtZXMobmV3X21hcmtlcnMpKT4xXSkKCnJicHNfbWFya2VycyA8LSByYnBzX21hcmtlcnNbIW5hbWVzKHJicHNfbWFya2VycykgJWluJSBuYW1lcyhuZXdfbWFya2VycyldCgpjb21iaW5lZF9tYXJrZXJzIDwtIGMocmJwc19tYXJrZXJzLCBuZXdfbWFya2VycykKcHJpbnQodGFibGUoY29tYmluZWRfbWFya2VycykpCnByaW50KHRhYmxlKG5hbWVzKGNvbWJpbmVkX21hcmtlcnMpKVt0YWJsZShuYW1lcyhjb21iaW5lZF9tYXJrZXJzKSk+MV0pCgpmRGF0YShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnApJG5ld19tYXJrZXJzIDwtIE5VTEwKcHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwIDwtIGFkZE1hcmtlcnMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCBjb21iaW5lZF9tYXJrZXJzLCAibmV3X21hcmtlcnMiKQoKZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwKSRuZXdfbWFya2VycyA8LSByZWNvZGUoCiAgZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwKSRuZXdfbWFya2VycywgIk5VQ0xFVVMiPSJOdWNsZXVzIiwgIlJJQk9TT01FIj0iUmlib3NvbWUiLCAiQ1lUT1NPTCI9IkN5dG9zb2wiLAogICJNSVRPQ0hPTkRSSUEiPSJNaXRvY2hvbmRyaWEiKSAKCnByaW50KHRhYmxlKGZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkkbmV3X21hcmtlcnMpKQpgYGAKQWZ0ZXIgYWRkaW5nIHRoZXNlIG5ldyBSQlAgbWFya2Vycywgd2Ugb25seSBoYXZlIDEgUE0gYW5kIDIgTXQgcHJvdGVpbnMgcmVtYWluaW5nLiBMZXQncyByZW1vdmUgdGhlIFBNIHByb3RlaW4KYGBge3J9CmZEYXRhKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCkkbmV3X21hcmtlcnNbZkRhdGEocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwKSRuZXdfbWFya2Vycz09IlBNIl0gPC0gInVua25vd24iCmBgYAoKYGBge3J9Cm5ld19tYXJrZXJzX2xldmVscyA8LSBnZXRNYXJrZXJDbGFzc2VzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIikKCnAgPC0gcGxvdFBDQShwcm90ZWluX3F1YW50X2FtX25vX2dseWNvX3llc19yYnAsICJuZXdfbWFya2VycyIsIHJlX29yZGVyX21hcmtlcnM9VFJVRSwgbWFya2VyX2xldmVscz1uZXdfbWFya2Vyc19sZXZlbHMsCiAgICAgICAgbV9jb2xvdXJzPWdldENsYXNzQ29sb3VycygpW2MoMTo1LCA3LCAxMDoyMCldLCBmb2k9bnVjbGVhcl9wb3JlX2NoYW5uZWwpCnByaW50KHApCgoKcCA8LSBwbG90UENBKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIiwgcmVfb3JkZXJfbWFya2Vycz1UUlVFLCBtYXJrZXJfbGV2ZWxzPW5ld19tYXJrZXJzX2xldmVscywKICAgICAgICBtX2NvbG91cnM9Z2V0Q2xhc3NDb2xvdXJzKClbYygxOjUsIDcsIDEwOjIwKV0sIGZvaT1udWNsZWFyX3BvcmVfYmFza2V0KQpwcmludChwKQoKYGBgCgoKYGBge3J9CmdldE1hcmtlckNsYXNzZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKQoKc2V0LnNlZWQoMSkKcHJvaiA8LSBtYWtlX3Byb2ooInQtU05FIiwgcHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KEhtaXNjKQpgYGAKCmBgYHtyfQpwcm9qX2RmIDwtIHByb2okUENBX2RmCm1hcmtlcl9sZXZlbHMgPC0gc2V0ZGlmZihuZXdfbWFya2Vyc19sZXZlbHMsICJ1bmtub3duIikKbWFya2VyX2xldmVscyA8LSBtYXJrZXJfbGV2ZWxzW2MoMiwzLDQsNiw4LDEsNSw3KV0KI21fY29sb3VycyA8LSBnZXRTdG9ja2NvbCgpW2MoMSw3LDQsMywyLDUpXQptX2NvbG91cnMgPC0gYyhjYlBhbGV0dGVbYyg2LDMsMiw0LDgsNyw1KV0sICJncmV5MjAiKQoKcHJval9kZiRtYXJrZXJzIDwtIGZhY3Rvcihwcm9qX2RmJG5ld19tYXJrZXJzLCBsZXZlbHM9bWFya2VyX2xldmVscykKcHJpbnQodGFibGUoaXMubmEocHJval9kZiRtYXJrZXJzKSkpCnByb2pfZGYkdW5rbm93biA8LSBwcm9qX2RmJG5ld19tYXJrZXJzPT0idW5rbm93biIKCnAgPC0gZ2dwbG90KHByb2pfZGYsIGFlcyhYLCBZLCBjb2xvdXI9bWFya2VycykpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsIFksIGNvbG91cj1tYXJrZXJzLCBhbHBoYT11bmtub3duKSwgc2l6ZT0yKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWMoMSwwLjIpLCBndWlkZT1GKSArCiAgICBteV90aGVtZSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1tX2NvbG91cnMsIG5hLnZhbHVlPSJncmV5NjAiLCBuYW1lPSIiLCBicmVha3M9YyhtYXJrZXJfbGV2ZWxzKSkgKwogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGFscGhhID0gMSwgc2l6ZT0yKSkpICsKICAgIHhsYWIoIkRpbWVuc2lvbiAxIikgKyB5bGFiKCJEaW1lbnNpb24gMiIpCgpwcmludChwKQpnZ3NhdmUoIi4vT3V0cHV0L3RTTkVfUkJQX21hcmtlcnMucG5nIikKd3JpdGUudGFibGUocHJval9kZiwgIi4vT3V0cHV0L3RTTkVfcHJvamVjdGlvbnMudHN2Iiwgc2VwPSJcdCIsIHF1b3RlPUZBTFNFLCByb3cubmFtZT1GQUxTRSkKCgpgYGAKYGBge3J9CnAgPC0gUGxvdE1hcmtlclByb2ZpbGVzKHByb3RlaW5fcXVhbnRfYW1fbm9fZ2x5Y29feWVzX3JicCwgIm5ld19tYXJrZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAga2VlcF9tYXJrZXJzPWdldE1hcmtlckNsYXNzZXMocHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwLCAibmV3X21hcmtlcnMiKSwgb3JnYW5lbGxlX29yZGVyPW5ld19tYXJrZXJzX2xldmVscykgKwogIGZhY2V0X3dyYXAofm1hcmtlcnMpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1nZXRDbGFzc0NvbG91cnMoKVtjKDE6NSwgNywgMTA6MjApXSwgZ3VpZGU9RkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwcmludChwKQpgYGAKYGBge3J9Cgpwcm90ZWluX2luZm8gPC0gcmVhZC5kZWxpbSgiSW5wdXQvQWdncmVnYXRlZC1wcm90ZWlucy0yMTg3LXdpdGgtdW5pcHJvdC50YWIiKSAlPiUKICBkcGx5cjo6c2VsZWN0KEVudHJ5LCBnZW5lX25hbWU9R2VuZS5uYW1lcy4uLnByaW1hcnkuLikKCnRvdGFsLnByb3QgPSByZWFkUkRTKCIuL0lucHV0L3Byb3RfcmVzXzIwX2ZyYWN0aW9uc19pbXB1dGVkX21hcmtlcnMucmRzIikKY29sbmFtZXModG90YWwucHJvdClbMTJdIDwtICIwLjk0OCIKY29sbmFtZXModG90YWwucHJvdCkgPC0gYyhzZXEoMSwyMCwyKSwgc2VxKDIsMjAsMikpCnRvdGFsLnByb3QgPC0gdG90YWwucHJvdFssb3JkZXIoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY29sbmFtZXModG90YWwucHJvdCkpKSldCnRvdGFsLnByb3QgPC0gdG90YWwucHJvdFssNToxOV0KdG90YWwucHJvdCA9IG5vcm1hbGlzZSh0b3RhbC5wcm90LCJzdW0iKQoKcmJwLnByb3QgPC0gcHJvdGVpbl9xdWFudF9hbV9ub19nbHljb195ZXNfcmJwCmNvbG5hbWVzKHJicC5wcm90KSA8LSA1OjE5CgpwcmludChkaW0odG90YWwucHJvdCkpCnByaW50KGRpbShyYnAucHJvdCkpCgpwcmludChjb2xuYW1lcyh0b3RhbC5wcm90KSkKcHJpbnQoY29sbmFtZXMocmJwLnByb3QpKQoKYGBgCgoKCmBgYHtyfQpwbG90Q29tYmluZWRQcm9maWxlcyA8LSBmdW5jdGlvbihmb2kpewogIAogIGZvaV9pbl90b3RhbCA8LSBpbnRlcnNlY3QoZm9pLCByb3duYW1lcyh0b3RhbC5wcm90KSkKICBmb2lfaW5fcmJwIDwtIGludGVyc2VjdChmb2ksIHJvd25hbWVzKHJicC5wcm90KSkKICAKICBmb2lfaW5fYm90aCA8LSBpbnRlcnNlY3QoZm9pX2luX3JicCwgZm9pX2luX3RvdGFsKQogIAogIHByaW50KGZvaV9pbl9ib3RoKQogIGlmKGxlbmd0aChmb2lfaW5fYm90aCk9PTApewogICAgcmV0dXJuKE5BKQogIH0KICAKICB0b3RhbF9leHByc19kZiA8LSBleHBycyh0b3RhbC5wcm90W2ZvaV9pbl9ib3RoLF0pCiAgdG90YWxfZXhwcnNfZGYgPC0gbWVsdCh0b3RhbF9leHByc19kZikKICB0b3RhbF9leHByc19kZiR0eXBlID0gIkFsbCBwcm90ZWluIgogIAogIHJicF9leHByc19kZiA8LSBleHBycyhyYnAucHJvdFtmb2lfaW5fYm90aCxdKQogIHJicF9leHByc19kZiA8LSBtZWx0KHJicF9leHByc19kZikKICByYnBfZXhwcnNfZGYkdHlwZSA9ICJSTkEtYm91bmQiCiAgCiAgI3ByaW50KGhlYWQodG90YWxfZXhwcnNfZGYpKQogICNwcmludChoZWFkKHJicF9leHByc19kZikpCiAgI3ByaW50KGhlYWQocmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikpKQogIAogIHAgPC0gcmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikgJT4lCiAgICBtZXJnZShwcm90ZWluX2luZm8sIGJ5Lng9IlZhcjEiLCBieS55PSJFbnRyeSIpICU+JQogICAgZ2dwbG90KGFlcyhWYXIyLCB2YWx1ZSwgY29sb3VyPXR5cGUsIGdyb3VwPXR5cGUpKSArCiAgICBteV90aGVtZSArIGdlb21fbGluZSgpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbz0xLCBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSwgaGp1c3Q9MSkpICArCiAgICB4bGFiKCJGcmFjdGlvbiIpICsgeWxhYigiU3VtbiBub3JtYWxpc2VkXG5hYnVuZGFuY2UiKSArIAogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWU9IiIsIG5hLnZhbHVlPSJncmV5IikKICAKICBpZihsZW5ndGgoZm9pX2luX2JvdGgpPjEpewogICAgcCA8LSBwICsgZmFjZXRfd3JhcCh+Z2VuZV9uYW1lKQogIH0KICAKICBwcmludChwKQogIAogIHAyIDwtIHAgPC0gcmJpbmQocmJwX2V4cHJzX2RmLCB0b3RhbF9leHByc19kZikgJT4lCiAgICBtZXJnZShwcm90ZWluX2luZm8sIGJ5Lng9IlZhcjEiLCBieS55PSJFbnRyeSIpICU+JQogICAgZ2dwbG90KGFlcyhWYXIyLCB2YWx1ZSwgY29sb3VyPXR5cGUsIGdyb3VwPWludGVyYWN0aW9uKHR5cGUsIGdlbmVfbmFtZSkpKSArCiAgICBteV90aGVtZSArIGdlb21fbGluZSgpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbz0xLCBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSwgaGp1c3Q9MSkpICArCiAgICB4bGFiKCJGcmFjdGlvbiIpICsgeWxhYigiU3VtbiBub3JtYWxpc2VkXG5hYnVuZGFuY2UiKSArIAogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWU9IiIsIG5hLnZhbHVlPSJncmV5IikKICAKICBwcmludChwMikKICAKICBpbnZpc2libGUobGlzdCgicCI9cCwgInAyIj1wMikpCn0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwbG90Q29tYmluZWRQcm9maWxlcyh0Uk5BX0FBKQojcGxvdENvbWJpbmVkUHJvZmlsZXMocGFyYVtbMl1dKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnBsb3RDb21iaW5lZFByb2ZpbGVzKG1vdG9yX2FjdGl2aXR5KQpgYGAKYGBge3J9CmZ2YXJMYWJlbHMocmJwLnByb3QpCndlbGxfcXVhbnRpZmllZF9yYnBzIDwtIGZEYXRhKHJicC5wcm90KSAlPiUKICBmaWx0ZXIoemVyb19pbXB1dGF0aW9uX248PTQsIG1pc3Npbmdfbjw9MikgJT4lCiAgcHVsbChtYXN0ZXJfcHJvdGVpbikKCnBsb3RDb21iaW5lZFByb2ZpbGVzKGludGVyc2VjdChtcm5hX3NwbGljaW5nLCB3ZWxsX3F1YW50aWZpZWRfcmJwcykpCmBgYApgYGB7cn0Kcmlib3NvbWVfcHJvdGVpbnMgPC0gbmFtZXMobWFya2Vyc19wcm90ZWlucylbbWFya2Vyc19wcm90ZWlucz09IlJJQk9TT01FIl0KcGxvdENvbWJpbmVkUHJvZmlsZXMoaW50ZXJzZWN0KHJpYm9zb21lX3Byb3RlaW5zLCB3ZWxsX3F1YW50aWZpZWRfcmJwcykpCmBgYApgYGB7cn0KcGxvdENvbWJpbmVkUHJvZmlsZXMoIlExNTk0MiIpCmBgYApgYGB7cn0KcCA8LSBwbG90Q29tYmluZWRQcm9maWxlcygiUDA5ODc0IikKCmdnc2F2ZSgiLi9PdXRwdXQvUEFSUDFfcHJvZmlsZXMucG5nIiwgcCRwMikKCmBgYAoK